use apikit::rpc::{
    WebsocketParamSign,
    ws_client::{FromMessage, IntoMessage},
};
use bytes::Bytes;
use reqwest_websocket::{CloseCode, Message};
use serde::{Serialize, de::DeserializeOwned};

use crate::param::{__TypeMapper, WsUpgrade};

pub struct WsMessage<T> {
    pub origin: Message,
    pub data: Option<T>,
}

impl<T: Serialize> WsMessage<T> {
    pub fn from_data(data: T) -> Self {
        Self {
            origin: Message::Text(serde_json::to_string(&data).unwrap()),
            data: Some(data),
        }
    }

    pub fn ping(content: Option<&str>) -> Self {
        match content {
            Some(content) => Self {
                origin: Message::Ping(content.as_bytes().to_vec().into()),
                data: None,
            },
            None => Self {
                origin: Message::Ping(Bytes::new()),
                data: None,
            },
        }
    }

    pub fn pong(content: Option<&str>) -> Self {
        match content {
            Some(content) => Self {
                origin: Message::Pong(content.as_bytes().to_vec().into()),
                data: None,
            },
            None => Self {
                origin: Message::Pong(Bytes::new()),
                data: None,
            },
        }
    }

    pub fn close(code: u16, reason: String) -> Self {
        Self {
            origin: Message::Close {
                code: CloseCode::from(code),
                reason,
            },
            data: None,
        }
    }
}

impl<S, R, SC, RC> WebsocketParamSign<WsUpgrade<S, R, SC, RC>> for __TypeMapper {
    type SendMessage = WsMessage<S>;

    type ReceiveMessage = WsMessage<R>;
}

impl<T: DeserializeOwned> FromMessage<Message> for WsMessage<T> {
    type Error = serde_json::Error;

    fn from_message(message: Message) -> Result<Self, Self::Error> {
        let mut data = None;
        if let Message::Text(text) = &message {
            data = serde_json::from_str(text)?;
        }
        Ok(Self {
            origin: message,
            data,
        })
    }
}

impl<T> IntoMessage<Message> for WsMessage<T> {
    type Error = serde_json::Error;

    fn into_message(self) -> Result<Message, Self::Error> {
        Ok(self.origin)
    }
}
